package com.czk.channel;

import org.junit.Test;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.charset.CharacterCodingException;
import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;
import java.nio.charset.CharsetEncoder;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.util.Map;
import java.util.Set;
import java.util.SortedMap;

/**
 * 一、 用于源节点和目标节点的连接，在 java nio负责缓冲区数据的传输，本身不存储数据，需要配合缓冲区进行数据传输
 * 二、主要实现类
 * FileChannel
 * socketChannel
 * ServerSocketChannel
 * DatagramChannel
 * 三、获取通道
 * 1 getChannel()方法
 * 2 jdk1.7中nio2为各个通道提供了静态方法open()
 * 四、通道之间的数据传输
 * transferFrom()
 * transferTo()
 * 五、分散于聚集
 * 分散读取：将通道中的数据分散到多个缓冲区中
 * 聚集写入：将多个缓冲区中的数据聚集到通道中
 * 五、字符集
 * 编码：字符串-》字节数组
 * 解码: 字符数组-》字符串
 *
 * @Author:ChenZhangKun
 * @Date: 2021/1/9 20:54
 */
public class ChannelTest {
    @Test
    public void test6() throws CharacterCodingException {
        Charset charset = Charset.forName("GBK");
        // 获取解码器
        CharsetDecoder charsetDecoder = charset.newDecoder();
        // 获取编码器
        CharsetEncoder charsetEncoder = charset.newEncoder();

        // 缓冲区
        CharBuffer allocate = CharBuffer.allocate(1024);
        allocate.put("czk");
        allocate.flip();
        // 编码
        ByteBuffer encode = charsetEncoder.encode(allocate);
        for (int i = 0; i < 3; i++) {
            // 每次读取一个字节
            System.out.println(encode.get());
        }
        // 转换为读取模式
        encode.flip();
        // 解码
        System.out.println(charsetDecoder.decode(encode));
    }

    /**
     * 获取全部字符集
     *
     * @throws Exception
     */
    @Test
    public void test5() throws Exception {
        SortedMap<String, Charset> stringCharsetSortedMap = Charset.availableCharsets();
        Set<Map.Entry<String, Charset>> entries = stringCharsetSortedMap.entrySet();
        for (Map.Entry<String, Charset> entry : entries) {
            System.out.println(entry.getKey() + "===" + entry.getValue());
        }
    }

    /**
     * 分散读取和聚集写入
     *
     * @throws IOException
     */
    @Test
    public void test4() throws IOException {
        RandomAccessFile raf1 = new RandomAccessFile("D:\\简历\\x.txt", "rw");
        // 获取通道
        FileChannel channel = raf1.getChannel();
        // 分配制定大小的缓冲区
        ByteBuffer allocate = ByteBuffer.allocate(100);
        ByteBuffer allocate1 = ByteBuffer.allocate(1024);

        // 分散读取
        ByteBuffer[] byteBuffers = {allocate, allocate1};
        channel.read(byteBuffers);
        // 将其设置为读模式
        for (ByteBuffer byteBuffer : byteBuffers) {
            byteBuffer.flip();
        }
        System.out.println(new String(byteBuffers[0].array(), 0, byteBuffers[0].limit(), "utf-8"));
        System.out.println(new String(byteBuffers[1].array(), 0, byteBuffers[1].limit()));
    }

    /**
     * channel之间的数据传输
     * 也是直接映射的方式
     *
     * @throws Exception
     */
    @Test
    public void test3() throws Exception {
        // 打开通道
        FileChannel inChannel = FileChannel.open(Paths.get("D:\\简历\\罗琴.doc"), StandardOpenOption.READ);
        // 添加读写模式
        FileChannel outChannel = FileChannel.open(Paths.get("D:\\简历\\1\\3.doc"), StandardOpenOption.READ, StandardOpenOption.WRITE, StandardOpenOption.CREATE);
        inChannel.transferTo(0, inChannel.size(), outChannel);
        // transferFrom
        // outChannel.transferFrom(inChannel,0,inChannel.size());
        inChannel.close();
        outChannel.close();
    }

    /**
     * 利用内存映射进行文件复制
     *
     * @throws Exception
     */
    @Test
    public void test2() throws Exception {
        // 打开通道
        FileChannel inChannel = FileChannel.open(Paths.get("D:\\简历\\罗琴.doc"), StandardOpenOption.READ);
        // 添加读写模式
        FileChannel outChannel = FileChannel.open(Paths.get("D:\\简历\\1\\2.doc"), StandardOpenOption.READ, StandardOpenOption.WRITE, StandardOpenOption.CREATE);
        // 内存映射文件
        MappedByteBuffer inMapBuffer = inChannel.map(FileChannel.MapMode.READ_ONLY, 0, inChannel.size());
        MappedByteBuffer outMapBuffer = outChannel.map(FileChannel.MapMode.READ_WRITE, 0, inChannel.size());
        // 直接对缓冲区进行数据的读写操作
        byte[] bytes = new byte[inMapBuffer.limit()];
        // 直接读
        inMapBuffer.get(bytes);
        outMapBuffer.put(bytes);
        outChannel.close();
        inChannel.close();

    }

    /**
     * 利用通道完成文件的复制
     * 非直接缓冲区
     */
    @Test
    public void test1() throws IOException {
        FileInputStream fis = new FileInputStream("D:\\简历\\罗琴.doc");
        FileOutputStream fos = new FileOutputStream("D:\\简历\\1\\1.doc");
        // 拿到通道
        FileChannel channel = fis.getChannel();
        FileChannel channel1 = fos.getChannel();
        // 获取buffer
        ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
        // 将通道数据写入缓冲区
        while (channel.read(byteBuffer) != -1) {
            // 将缓冲区切换成读数据模式
            byteBuffer.flip();
            // 写入数据
            channel1.write(byteBuffer);
            // 清空缓冲区
            byteBuffer.clear();
        }
        // 关闭
        channel1.close();
        channel.close();
        fos.close();
        fis.close();
    }
}
